package tij4.concurrent.source;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

/*
    饭店业务流程:
        顾客点餐--->服务员生成订单--->厨师拿到订单--->厨师做好菜--->服务员端盘子--->客户拿到菜--->客户吃菜
        名词,固有的,不是动作
            顾客,服务员,订单,厨师,菜,盘子,饭店
            做成类
*/

/*
    加个桌子,客户坐在桌子上
    饭店业务流程:
        服务员给客户安排桌子--->顾客点餐--->服务员生成订单--->厨师拿到订单--->厨师做好菜--->服务员端盘子--->客户拿到菜--->客户吃菜
        名词,固有的,不是动作
            顾客,服务员,订单,厨师,菜,盘子,饭店,桌子(限定人数)
            做成类
*/

/*
    订单
 */
class Order {
    private static int counter = 0;
    private final int id = counter++;
    private final Customer1 customer;
    private final WaitPerson1 waitPerson;
    private final Food food;

    public Order(Customer1 customer, WaitPerson1 waitPerson, Food food) {
        this.customer = customer;
        this.waitPerson = waitPerson;
        this.food = food;
    }
    public Food item() {
        return food;
    }

    public Customer1 getCustomer() {
        return customer;
    }

    public WaitPerson1 getWaitPerson() {
        return waitPerson;
    }

    @Override
    public String toString() {
        return "Order: " + id + " item: " + food
                + " for : " + customer + "served by: " + waitPerson;
    }
}

/*
    碟子
 */
class Plate {
    private final Order order;
    private final Food food;

    public Plate(Order order, Food food) {
        this.order = order;
        this.food = food;
    }
    public Food getFood() {
        return food;
    }
    public Order getOrder() {
        return order;
    }
    @Override
    public String toString() {
        return food.toString();
    }
}

/*
    客户
 */
class Customer1 implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private final WaitPerson1 waitPerson;
    //一次只能接受一次
    private SynchronousQueue<Plate> placeSetting = new SynchronousQueue<>();

    public Customer1(WaitPerson1 waitPerson) {
        this.waitPerson = waitPerson;
    }
    public void deliver(Plate p) throws InterruptedException {  //菜来了
        //如果客户在在吃之前的则会阻塞
        placeSetting.put(p);
    }

    @Override
    public void run() {
        for (Course course : Course.values()) {
            Food food = course.randomSelection();
            try {
                waitPerson.placeOrder(this, food);  //客户点菜
                System.out.println(this + "eating  " + placeSetting.take());    //客户吃菜
            }catch (InterruptedException e) {
                System.out.println(this + "waiting for " + course + " interrupted");
            }
        }
        System.out.println(this + "finished meal, leaving");
    }

    @Override
    public String toString() {
        return "Customer " + id + " ";
    }
}

/*
    服务员
 */
class WaitPerson1 implements Runnable {
    private static int counter;
    private final int id = counter++;
    private final Restaurant1 restaurant;
    BlockingQueue<Plate> filledOrders = new LinkedBlockingQueue<>();

    public WaitPerson1(Restaurant1 restaurant) {
        this.restaurant = restaurant;
    }
    public void placeOrder(Customer1 customer, Food food) { //客户点菜
        try {
            restaurant.orders.put(new Order(customer, this, food)); //服务员生成订单
        }catch (InterruptedException e) {
            System.out.println(this + " placeOrder interrupted");
        }
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Plate plate = filledOrders.take();  //服务员得到菜
                System.out.println(this + "recevice " + plate + " delivering to " +
                        plate.getOrder().getCustomer());
                plate.getOrder().getCustomer().deliver(plate);  //put 给客户上菜

            }
        }catch (InterruptedException e) {
            System.out.println(this + " interrupted");
        }
        System.out.println(this + " off duty");
    }

    @Override
    public String toString() {
        return "WaitPerson " + id + " ";
    }
}

/*
    做菜
 */
class Chef1 implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private final Restaurant1 restaurant;
    private static Random random = new Random(47);

    public Chef1(Restaurant1 restaurant) {
        this.restaurant = restaurant;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Order order = restaurant.orders.take(); //厨师拿到订单
                Food requestedItem = order.item();
                TimeUnit.MILLISECONDS.sleep(random.nextInt(500));
                Plate plate = new Plate(order, requestedItem);
                order.getWaitPerson().filledOrders.put(plate);  //厨师给服务器菜
            }
        }catch (InterruptedException e) {
            System.out.println(this + " interrupted");
        }
        System.out.println(this + " off duty");
    }

    @Override
    public String toString() {
        return "Chef1 " + id + " ";
    }
}

/*
    餐馆
 */
class Restaurant1 implements Runnable {
    private List<WaitPerson1> waitPersonList = new ArrayList<>();
    private List<Chef1> chefList = new ArrayList<>();
    private ExecutorService executorService;
    private static Random random = new Random(47);
    BlockingQueue<Order> orders = new LinkedBlockingQueue<>();

    public Restaurant1(ExecutorService executorService, int nWaitPersons, int nChefs) {
        this.executorService = executorService;
        for (int i = 0; i < nWaitPersons; i++) {
            WaitPerson1 waitPerson = new WaitPerson1(this);
            waitPersonList.add(waitPerson);
            executorService.execute(waitPerson);
        }
        for (int i = 0; i < nChefs; i++) {
            Chef1 chef = new Chef1(this);
            chefList.add(chef);
            executorService.execute(chef);
        }
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                WaitPerson1 waitPerson1 = waitPersonList.get(random.nextInt(waitPersonList.size()));
                Customer1 customer = new Customer1(waitPerson1);
                executorService.execute(customer);
                TimeUnit.MILLISECONDS.sleep(100);
            }
        }catch (InterruptedException e) {
            System.out.println("Restaurant interrupted");
        }
        System.out.println("Restaurant closing");
    }
}
public class RestaurantWithQueues {
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Restaurant1 restaurant = new Restaurant1(executorService, 5, 2);
        executorService.execute(restaurant);
        TimeUnit.SECONDS.sleep(5);
        executorService.shutdownNow();
    }
}
